﻿/************************************************************************************ 
 * Copyright (c) 2008-2011, Columbia University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Columbia University nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY COLUMBIA UNIVERSITY ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 * ===================================================================================
 * Author: Ohan Oda (ohan@cs.columbia.edu)
 * 
 * ===================================================================================
 * 
 * Modified by Hristo Hristov
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 * 
 * Revision: 5
 * 
 * UPDATE on 23.06.11: -Support of textures by changing vertex type from 
 *                      "VertexPositionNormal" to "VertexPositionNormalTexture" 
 *                      and assign the corresponding texture coordinates.
 * 
 * UPDATE on 04.06.11: -Support of Normal- and parallax-mapping by changing the 
 *                      vertex type to "NormalMappedVertex", which includes all
 *                      vertex information needed.
 * 
 * UPDATE on 13.07.11: -Changed the string format of the parameters of the
 *                      "customShapeParameters" from "xx,yy" to "xx.yy", which
 *                      would otherwise cause a bug in GoblinXNA, when loading a 
 *                      scene from a xml file, because due to the comma a float 
 *                      would be recognized as to individual integers.
 *************************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using System.Globalization;

namespace NOVA.Graphics
{
    /// <summary>
    /// A box geometry primitive constructed with GoblinXNA-CustomMesh and DHPOWare-NormalMappedVertices
    /// </summary>
    public class Box : PrimitiveModel
    {
        #region Properties

        public string ShapeProperties
        {
            get { return customShapeParameters; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Create a box with the given dimensions.
        /// </summary>
        /// <param name="xdim">X dimension</param>
        /// <param name="ydim">Y dimension</param>
        /// <param name="zdim">Z dimension</param>
        public Box(float xdim, float ydim, float zdim)
            : base(CreateBox(xdim, ydim, zdim))
        {
            // Create the parameter string and force the use of a dot as separator to avoid a bug during loading
            customShapeParameters = xdim.ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    ydim.ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    zdim.ToString(CultureInfo.InvariantCulture.NumberFormat);
        }

        /// <summary>
        /// Creates a box whose edges are the given length.
        /// </summary>
        /// <param name="length">Length of each side of the cube</param>
        public Box(float length)
            : base(CreateBox(length, length, length))
        {
            customShapeParameters = length.ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    length.ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    length.ToString(CultureInfo.InvariantCulture.NumberFormat);
        }

        /// <summary>
        /// Create a box with the given dimensions.
        /// </summary>
        /// <param name="dimension">Dimension of the box geometry</param>
        public Box(Vector3 dimension)
            : base(CreateBox(dimension.X, dimension.Y, dimension.Z))
        {
            customShapeParameters = dimension.X.ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    dimension.Y.ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    dimension.Z.ToString(CultureInfo.InvariantCulture.NumberFormat);
        }

        /// <summary>
        /// Create a box from an XML input.
        /// </summary>
        /// <param name="xmlParams"></param>
        public Box(params String[] xmlParams)
            : base(CreateBox(float.Parse(xmlParams[0]), float.Parse(xmlParams[1]),
                float.Parse(xmlParams[2])))
        {
            customShapeParameters = xmlParams[0].ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    xmlParams[1].ToString(CultureInfo.InvariantCulture.NumberFormat) + ", " +
                                    xmlParams[2].ToString(CultureInfo.InvariantCulture.NumberFormat);
        }

        #endregion

        #region Private Static Method

        private static CustomMesh CreateBox(float xdim, float ydim, float zdim)
        {
            /* 0--------1
             * |\      /|
             * | 4----5 |
             * | |    | |
             * | |    | |
             * | 7----6 |
             * |/      \|
             * 3--------2
             *
             * +z face: 0123 tri1: 012 tri2: 230
             * -z face: 5476 tri1: 547 tri2: 765
             *
             * +y face: 4510 tri1: 451 tri2: 104
             * -y face: 3267 tri1: 326 tri2: 673
             *
             * +x face: 1562 tri1: 156 tri2: 621
             * -x face: 4037 tri1: 403 tri2: 374
             */

            CustomMesh mesh = new CustomMesh();

            NormalMappedVertex[] vertices = new NormalMappedVertex[36];
            Vector3 halfExtent = new Vector3();

            halfExtent.X = xdim / 2;
            halfExtent.Y = ydim / 2;
            halfExtent.Z = zdim / 2;

            Vector3[] corners =
            {
                new Vector3(-halfExtent.X,  halfExtent.Y,  halfExtent.Z),   // 0
                new Vector3( halfExtent.X,  halfExtent.Y,  halfExtent.Z),   // 1
                new Vector3( halfExtent.X, -halfExtent.Y,  halfExtent.Z),   // 2
                new Vector3(-halfExtent.X, -halfExtent.Y,  halfExtent.Z),   // 3
                
                new Vector3(-halfExtent.X,  halfExtent.Y, -halfExtent.Z),   // 4
                new Vector3( halfExtent.X,  halfExtent.Y, -halfExtent.Z),   // 5
                new Vector3( halfExtent.X, -halfExtent.Y, -halfExtent.Z),   // 6
                new Vector3(-halfExtent.X, -halfExtent.Y, -halfExtent.Z),   // 7
            };

            Vector2[] texCoords =
            {
                new Vector2(0.0f, 0.0f),    // top left corner
                new Vector2(1.0f, 0.0f),    // top right corner
                new Vector2(1.0f, 1.0f),    // bottom right corner    
                new Vector2(0.0f, 1.0f),    // bottom left corner
            };

            // +z face: 0123 tri1: 012 tri2: 230
            vertices[0] = new NormalMappedVertex(corners[0], texCoords[0], Vector3.Backward, Vector4.Zero, Vector3.Zero);
            vertices[1] = new NormalMappedVertex(corners[1], texCoords[1], Vector3.Backward, Vector4.Zero, Vector3.Zero);
            vertices[2] = new NormalMappedVertex(corners[2], texCoords[2], Vector3.Backward, Vector4.Zero, Vector3.Zero);
            vertices[3] = new NormalMappedVertex(corners[2], texCoords[2], Vector3.Backward, Vector4.Zero, Vector3.Zero);
            vertices[4] = new NormalMappedVertex(corners[3], texCoords[3], Vector3.Backward, Vector4.Zero, Vector3.Zero);
            vertices[5] = new NormalMappedVertex(corners[0], texCoords[0], Vector3.Backward, Vector4.Zero, Vector3.Zero);

            // -z face: 5476 tri1: 547 tri2: 765
            vertices[6] = new NormalMappedVertex(corners[5], texCoords[0], Vector3.Forward, Vector4.Zero, Vector3.Zero);
            vertices[7] = new NormalMappedVertex(corners[4], texCoords[1], Vector3.Forward, Vector4.Zero, Vector3.Zero);
            vertices[8] = new NormalMappedVertex(corners[7], texCoords[2], Vector3.Forward, Vector4.Zero, Vector3.Zero);
            vertices[9] = new NormalMappedVertex(corners[7], texCoords[2], Vector3.Forward, Vector4.Zero, Vector3.Zero);
            vertices[10] = new NormalMappedVertex(corners[6], texCoords[3], Vector3.Forward, Vector4.Zero, Vector3.Zero);
            vertices[11] = new NormalMappedVertex(corners[5], texCoords[0], Vector3.Forward, Vector4.Zero, Vector3.Zero);

            // +y face: 4510 tri1: 451 tri2: 104
            vertices[12] = new NormalMappedVertex(corners[4], texCoords[0], Vector3.Up, Vector4.Zero, Vector3.Zero);
            vertices[13] = new NormalMappedVertex(corners[5], texCoords[1], Vector3.Up, Vector4.Zero, Vector3.Zero);
            vertices[14] = new NormalMappedVertex(corners[1], texCoords[2], Vector3.Up, Vector4.Zero, Vector3.Zero);
            vertices[15] = new NormalMappedVertex(corners[1], texCoords[2], Vector3.Up, Vector4.Zero, Vector3.Zero);
            vertices[16] = new NormalMappedVertex(corners[0], texCoords[3], Vector3.Up, Vector4.Zero, Vector3.Zero);
            vertices[17] = new NormalMappedVertex(corners[4], texCoords[0], Vector3.Up, Vector4.Zero, Vector3.Zero);

            // -y face: 3267 tri1: 326 tri2: 673
            vertices[18] = new NormalMappedVertex(corners[3], texCoords[0], Vector3.Down, Vector4.Zero, Vector3.Zero);
            vertices[19] = new NormalMappedVertex(corners[2], texCoords[1], Vector3.Down, Vector4.Zero, Vector3.Zero);
            vertices[20] = new NormalMappedVertex(corners[6], texCoords[2], Vector3.Down, Vector4.Zero, Vector3.Zero);
            vertices[21] = new NormalMappedVertex(corners[6], texCoords[2], Vector3.Down, Vector4.Zero, Vector3.Zero);
            vertices[22] = new NormalMappedVertex(corners[7], texCoords[3], Vector3.Down, Vector4.Zero, Vector3.Zero);
            vertices[23] = new NormalMappedVertex(corners[3], texCoords[0], Vector3.Down, Vector4.Zero, Vector3.Zero);

            // +x face: 1562 tri1: 156 tri2: 621
            vertices[24] = new NormalMappedVertex(corners[1], texCoords[0], Vector3.Right, Vector4.Zero, Vector3.Zero);
            vertices[25] = new NormalMappedVertex(corners[5], texCoords[1], Vector3.Right, Vector4.Zero, Vector3.Zero);
            vertices[26] = new NormalMappedVertex(corners[6], texCoords[2], Vector3.Right, Vector4.Zero, Vector3.Zero);
            vertices[27] = new NormalMappedVertex(corners[6], texCoords[2], Vector3.Right, Vector4.Zero, Vector3.Zero);
            vertices[28] = new NormalMappedVertex(corners[2], texCoords[3], Vector3.Right, Vector4.Zero, Vector3.Zero);
            vertices[29] = new NormalMappedVertex(corners[1], texCoords[0], Vector3.Right, Vector4.Zero, Vector3.Zero);

            // -x face: 4037 tri1: 403 tri2: 374
            vertices[30] = new NormalMappedVertex(corners[4], texCoords[0], Vector3.Left, Vector4.Zero, Vector3.Zero);
            vertices[31] = new NormalMappedVertex(corners[0], texCoords[1], Vector3.Left, Vector4.Zero, Vector3.Zero);
            vertices[32] = new NormalMappedVertex(corners[3], texCoords[2], Vector3.Left, Vector4.Zero, Vector3.Zero);
            vertices[33] = new NormalMappedVertex(corners[3], texCoords[2], Vector3.Left, Vector4.Zero, Vector3.Zero);
            vertices[34] = new NormalMappedVertex(corners[7], texCoords[3], Vector3.Left, Vector4.Zero, Vector3.Zero);
            vertices[35] = new NormalMappedVertex(corners[4], texCoords[0], Vector3.Left, Vector4.Zero, Vector3.Zero);

            // Calculate the tangent space basis vectors for the cube faces.
            Vector4 tangent;
            Vector3 binormal;

            for (int i = 0; i < 36; i += 3)
            {
                NormalMappedVertex.CalcTangent(
                    ref vertices[i].Position,
                    ref vertices[i + 1].Position,
                    ref vertices[i + 2].Position,
                    ref vertices[i].TexCoord,
                    ref vertices[i + 1].TexCoord,
                    ref vertices[i + 2].TexCoord,
                    ref vertices[i].Normal,
                    out tangent,
                    out binormal);

                vertices[i].Tangent = tangent;
                vertices[i + 1].Tangent = tangent;
                vertices[i + 2].Tangent = tangent;

                vertices[i].Binormal = binormal;
                vertices[i + 1].Binormal = binormal;
                vertices[i + 2].Binormal = binormal;
            }

            ////////////////////////////////////////////////////////////////////////////
            // Assign the mesh properties

            mesh.VertexDeclaration = NormalMappedVertex.VertexDeclaration;

            mesh.VertexBuffer = new VertexBuffer(Core.Device, typeof(NormalMappedVertex), vertices.Length, BufferUsage.None);
            mesh.VertexBuffer.SetData<NormalMappedVertex>(vertices);
            
            short[] indices = new short[36];
            
            // Setup the indices for a basic triangle list
            for (short i = 0; i < indices.Length; i++)
                indices[i] = i;

            mesh.IndexBuffer = new IndexBuffer(Core.Device, typeof(short), indices.Length, BufferUsage.None);
            mesh.IndexBuffer.SetData(indices);

            mesh.NumberOfVertices = vertices.Length;
            mesh.NumberOfPrimitives = 12;

            return mesh;
        }

        #endregion
    }

#if OLD
    /// <summary>
    /// A box geometry primitive constructed with CustomMesh
    /// </summary>
    public class Box : PrimitiveModel
    {
        #region Constructors

        /// <summary>
        /// Create a box with the given dimensions.
        /// </summary>
        /// <param name="xdim">X dimension</param>
        /// <param name="ydim">Y dimension</param>
        /// <param name="zdim">Z dimension</param>
        public Box(float xdim, float ydim, float zdim)
            : base(CreateBox(xdim, ydim, zdim))
        {
            customShapeParameters = xdim + ", " + ydim + ", " + zdim;
        }

        /// <summary>
        /// Creates a box whose edges are the given length.
        /// </summary>
        /// <param name="length">Length of each side of the cube</param>
        public Box(float length)
            : base(CreateBox(length, length, length))
        {
            customShapeParameters = length + ", " + length + ", " + length;
        }

        /// <summary>
        /// Create a box with the given dimensions.
        /// </summary>
        /// <param name="dimension">Dimension of the box geometry</param>
        public Box(Vector3 dimension)
            : base(CreateBox(dimension.X, dimension.Y, dimension.Z))
        {
            customShapeParameters = dimension.X + ", " + dimension.Y + ", " + dimension.Z;
        }

        /// <summary>
        /// Create a box from an XML input.
        /// </summary>
        /// <param name="xmlParams"></param>
        public Box(params String[] xmlParams)
            : base(CreateBox(float.Parse(xmlParams[0]), float.Parse(xmlParams[1]),
                float.Parse(xmlParams[2])))
        {
            customShapeParameters = xmlParams[0] + ", " + xmlParams[1] + ", " + xmlParams[2];
        }

        #endregion

        #region Private Static Method

        private static CustomMesh CreateBox(float xdim, float ydim, float zdim)
        {
            CustomMesh mesh = new CustomMesh();
            
            VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[24];
            Vector3 halfExtent = new Vector3();

            halfExtent.X = xdim / 2;
            halfExtent.Y = ydim / 2;
            halfExtent.Z = zdim / 2;

            Vector3 v0 = Vector3Helper.Get(-halfExtent.X, -halfExtent.Y, -halfExtent.Z);
            Vector3 v1 = Vector3Helper.Get(halfExtent.X, -halfExtent.Y, -halfExtent.Z);
            Vector3 v2 = Vector3Helper.Get(-halfExtent.X, halfExtent.Y, -halfExtent.Z);
            Vector3 v3 = Vector3Helper.Get(halfExtent.X, halfExtent.Y, -halfExtent.Z);
            Vector3 v4 = Vector3Helper.Get(halfExtent.X, halfExtent.Y, halfExtent.Z);
            Vector3 v5 = Vector3Helper.Get(-halfExtent.X, halfExtent.Y, halfExtent.Z);
            Vector3 v6 = Vector3Helper.Get(halfExtent.X, -halfExtent.Y, halfExtent.Z);
            Vector3 v7 = Vector3Helper.Get(-halfExtent.X, -halfExtent.Y, halfExtent.Z);

            Vector3 nZ = -Vector3.UnitZ;
            Vector3 pZ = Vector3.UnitZ;
            Vector3 nX = -Vector3.UnitX;
            Vector3 pX = Vector3.UnitX;
            Vector3 nY = -Vector3.UnitY;
            Vector3 pY = Vector3.UnitY;

            ////////////////////////////////////////////////////////////////////////////
            // Set the vertex positions

            // Back plane
            vertices[0].Position = v0; vertices[1].Position = v1;
            vertices[2].Position = v2; vertices[3].Position = v3;

            // Left plane
            vertices[4].Position = v0; vertices[5].Position = v7;
            vertices[6].Position = v2; vertices[7].Position = v5;

            // Front plane
            vertices[8].Position = v4; vertices[9].Position = v5;
            vertices[10].Position = v7; vertices[11].Position = v6;

            // Right plane
            vertices[12].Position = v4; vertices[13].Position = v3;
            vertices[14].Position = v1; vertices[15].Position = v6;

            // Top plane
            vertices[16].Position = v2; vertices[17].Position = v4;
            vertices[18].Position = v5; vertices[19].Position = v3;

            // Bottom plane
            vertices[20].Position = v0; vertices[21].Position = v1;
            vertices[22].Position = v6; vertices[23].Position = v7;

            ////////////////////////////////////////////////////////////////////////////
            // Set the vertex normals
            for (int i = 0; i < 4; i++)     // Back plane
                vertices[i].Normal = nZ;
            for (int i = 4; i < 8; i++)     // Left plane
                vertices[i].Normal = nX;
            for (int i = 8; i < 12; i++)    // Front plane
                vertices[i].Normal = pZ;
            for (int i = 12; i < 16; i++)   // Right plane
                vertices[i].Normal = pX;
            for (int i = 16; i < 20; i++)   // Top plane
                vertices[i].Normal = pY;
            for (int i = 20; i < 24; i++)   // Bottom plane
                vertices[i].Normal = nY;

            ////////////////////////////////////////////////////////////////////////////
            // Set texture coordinates

            // Back plane
            vertices[0].TextureCoordinate = new Vector2(1, 1);
            vertices[1].TextureCoordinate = new Vector2(0, 1);
            vertices[2].TextureCoordinate = new Vector2(1, 0);
            vertices[3].TextureCoordinate = new Vector2(0, 0);

            // Left plane
            vertices[4].TextureCoordinate = new Vector2(0, 1);
            vertices[5].TextureCoordinate = new Vector2(1, 1);
            vertices[6].TextureCoordinate = new Vector2(0, 0);
            vertices[7].TextureCoordinate = new Vector2(1, 0);

            // Front plane
            vertices[8].TextureCoordinate = new Vector2(1, 0);
            vertices[9].TextureCoordinate = new Vector2(0, 0);
            vertices[10].TextureCoordinate = new Vector2(0, 1);
            vertices[11].TextureCoordinate = new Vector2(1, 1);

            // Right plane
            vertices[12].TextureCoordinate = new Vector2(0, 0);
            vertices[13].TextureCoordinate = new Vector2(1, 0);
            vertices[14].TextureCoordinate = new Vector2(1, 1);
            vertices[15].TextureCoordinate = new Vector2(0, 1);

            // Top plane
            vertices[16].TextureCoordinate = new Vector2(0, 0);
            vertices[17].TextureCoordinate = new Vector2(1, 1);
            vertices[18].TextureCoordinate = new Vector2(0, 1);
            vertices[19].TextureCoordinate = new Vector2(1, 0);

            // Bottom plane
            vertices[20].TextureCoordinate = new Vector2(0, 0);
            vertices[21].TextureCoordinate = new Vector2(1, 0);
            vertices[22].TextureCoordinate = new Vector2(1, 1);
            vertices[23].TextureCoordinate = new Vector2(0, 1);

            ////////////////////////////////////////////////////////////////////////////
            // Assign the mesh properties

            mesh.VertexDeclaration = VertexPositionNormalTexture.VertexDeclaration;

            mesh.VertexBuffer = new VertexBuffer(State.Device, typeof(VertexPositionNormalTexture),
                24, BufferUsage.None);
            mesh.VertexBuffer.SetData<VertexPositionNormalTexture>(vertices);

            short[] indices = new short[36];

            indices[0] = 0; indices[1] = 1; indices[2] = 2;
            indices[3] = 2; indices[4] = 1; indices[5] = 3;
            indices[6] = 4; indices[7] = 6; indices[8] = 5;
            indices[9] = 6; indices[10] = 7; indices[11] = 5;
            indices[12] = 11; indices[13] = 10; indices[14] = 9;
            indices[15] = 11; indices[16] = 9; indices[17] = 8;
            indices[18] = 14; indices[19] = 15; indices[20] = 13;
            indices[21] = 15; indices[22] = 12; indices[23] = 13;
            indices[24] = 19; indices[25] = 17; indices[26] = 18;
            indices[27] = 19; indices[28] = 18; indices[29] = 16;
            indices[30] = 21; indices[31] = 20; indices[32] = 23;
            indices[33] = 21; indices[34] = 23; indices[35] = 22;

            mesh.IndexBuffer = new IndexBuffer(State.Device, typeof(short), 36,
                BufferUsage.None);
            mesh.IndexBuffer.SetData(indices);

            mesh.NumberOfVertices = 24;
            mesh.NumberOfPrimitives = 12;

            return mesh;
        }

        #endregion
    }
#endif
}
